@@ -21,6 +21,7 @@ class Agent < ActiveRecord::Base |
||
| 21 | 21 |
attr_accessible :options, :memory, :name, :type, :schedule, :source_ids, :keep_events_for |
| 22 | 22 |
|
| 23 | 23 |
validates_presence_of :name, :user |
| 24 |
+ validates_inclusion_of :keep_events_for, :in => EVENT_RETENTION_SCHEDULES.map(&:last) |
|
| 24 | 25 |
validate :sources_are_owned |
| 25 | 26 |
validate :validate_schedule |
| 26 | 27 |
|
@@ -29,6 +30,7 @@ class Agent < ActiveRecord::Base |
||
| 29 | 30 |
before_validation :unschedule_if_cannot_schedule |
| 30 | 31 |
before_save :unschedule_if_cannot_schedule |
| 31 | 32 |
before_create :set_last_checked_event_id |
| 33 |
+ after_save :possibly_update_event_expirations |
|
| 32 | 34 |
|
| 33 | 35 |
belongs_to :user, :inverse_of => :agents |
| 34 | 36 |
has_many :events, :dependent => :delete_all, :inverse_of => :agent, :order => "events.id desc" |
@@ -175,6 +177,18 @@ class Agent < ActiveRecord::Base |
||
| 175 | 177 |
end |
| 176 | 178 |
end |
| 177 | 179 |
|
| 180 |
+ def possibly_update_event_expirations |
|
| 181 |
+ update_event_expirations! if keep_events_for_changed? |
|
| 182 |
+ end |
|
| 183 |
+ |
|
| 184 |
+ def update_event_expirations! |
|
| 185 |
+ if keep_events_for == 0 |
|
| 186 |
+ events.update_all :expires_at => nil |
|
| 187 |
+ else |
|
| 188 |
+ events.update_all "expires_at = DATE_ADD(`created_at`, INTERVAL #{keep_events_for.to_i} DAY)"
|
|
| 189 |
+ end |
|
| 190 |
+ end |
|
| 191 |
+ |
|
| 178 | 192 |
# Class Methods |
| 179 | 193 |
class << self |
| 180 | 194 |
def cannot_be_scheduled! |
@@ -249,7 +249,7 @@ describe Agent do |
||
| 249 | 249 |
agent.should have(0).errors_on(:base) |
| 250 | 250 |
end |
| 251 | 251 |
|
| 252 |
- it "symbolizes options before validating" do |
|
| 252 |
+ it "makes options symbol-indifferent before validating" do |
|
| 253 | 253 |
agent = Agents::SomethingSource.new(:name => "something") |
| 254 | 254 |
agent.user = users(:bob) |
| 255 | 255 |
agent.options["bad"] = true |
@@ -258,7 +258,7 @@ describe Agent do |
||
| 258 | 258 |
agent.should have(0).errors_on(:base) |
| 259 | 259 |
end |
| 260 | 260 |
|
| 261 |
- it "symbolizes memory before validating" do |
|
| 261 |
+ it "makes memory symbol-indifferent before validating" do |
|
| 262 | 262 |
agent = Agents::SomethingSource.new(:name => "something") |
| 263 | 263 |
agent.user = users(:bob) |
| 264 | 264 |
agent.memory["bad"] = :hello |
@@ -276,7 +276,85 @@ describe Agent do |
||
| 276 | 276 |
agent.user = users(:jane) |
| 277 | 277 |
agent.should have(0).errors_on(:sources) |
| 278 | 278 |
end |
| 279 |
+ |
|
| 280 |
+ it "validates keep_events_for" do |
|
| 281 |
+ agent = Agents::SomethingSource.new(:name => "something") |
|
| 282 |
+ agent.user = users(:bob) |
|
| 283 |
+ agent.should be_valid |
|
| 284 |
+ agent.keep_events_for = nil |
|
| 285 |
+ agent.should have(1).errors_on(:keep_events_for) |
|
| 286 |
+ agent.keep_events_for = 1000 |
|
| 287 |
+ agent.should have(1).errors_on(:keep_events_for) |
|
| 288 |
+ agent.keep_events_for = "" |
|
| 289 |
+ agent.should have(1).errors_on(:keep_events_for) |
|
| 290 |
+ agent.keep_events_for = 5 |
|
| 291 |
+ agent.should be_valid |
|
| 292 |
+ agent.keep_events_for = 0 |
|
| 293 |
+ agent.should be_valid |
|
| 294 |
+ agent.keep_events_for = 365 |
|
| 295 |
+ agent.should be_valid |
|
| 296 |
+ |
|
| 297 |
+ # Rails seems to call to_i on the input. This guards against future changes to that behavior. |
|
| 298 |
+ agent.keep_events_for = "drop table;" |
|
| 299 |
+ agent.keep_events_for.should == 0 |
|
| 300 |
+ end |
|
| 301 |
+ end |
|
| 302 |
+ |
|
| 303 |
+ describe "cleaning up now-expired events" do |
|
| 304 |
+ before do |
|
| 305 |
+ @agent = Agents::SomethingSource.new(:name => "something") |
|
| 306 |
+ @agent.keep_events_for = 5 |
|
| 307 |
+ @agent.user = users(:bob) |
|
| 308 |
+ @agent.save! |
|
| 309 |
+ @event = @agent.create_event :payload => { "hello" => "world" }
|
|
| 310 |
+ @event.expires_at.to_i.should be_within(2).of(5.days.from_now.to_i) |
|
| 311 |
+ end |
|
| 312 |
+ |
|
| 313 |
+ describe "when keep_events_for has not changed" do |
|
| 314 |
+ it "does nothing" do |
|
| 315 |
+ mock(@agent).update_event_expirations!.times(0) |
|
| 316 |
+ |
|
| 317 |
+ @agent.options[:foo] = "bar1" |
|
| 318 |
+ @agent.save! |
|
| 319 |
+ |
|
| 320 |
+ @agent.options[:foo] = "bar1" |
|
| 321 |
+ @agent.keep_events_for = 5 |
|
| 322 |
+ @agent.save! |
|
| 323 |
+ end |
|
| 324 |
+ end |
|
| 325 |
+ |
|
| 326 |
+ describe "when keep_events_for is changed" do |
|
| 327 |
+ it "updates events' expires_at" do |
|
| 328 |
+ lambda {
|
|
| 329 |
+ @agent.options[:foo] = "bar1" |
|
| 330 |
+ @agent.keep_events_for = 3 |
|
| 331 |
+ @agent.save! |
|
| 332 |
+ }.should change { @event.reload.expires_at }
|
|
| 333 |
+ @event.expires_at.to_i.should be_within(2).of(3.days.from_now.to_i) |
|
| 334 |
+ end |
|
| 335 |
+ |
|
| 336 |
+ it "updates events relative to their created_at" do |
|
| 337 |
+ @event.update_attribute :created_at, 2.days.ago |
|
| 338 |
+ @event.reload.created_at.to_i.should be_within(2).of(2.days.ago.to_i) |
|
| 339 |
+ |
|
| 340 |
+ lambda {
|
|
| 341 |
+ @agent.options[:foo] = "bar2" |
|
| 342 |
+ @agent.keep_events_for = 3 |
|
| 343 |
+ @agent.save! |
|
| 344 |
+ }.should change { @event.reload.expires_at }
|
|
| 345 |
+ @event.expires_at.to_i.should be_within(2).of(1.days.from_now.to_i) |
|
| 346 |
+ end |
|
| 347 |
+ |
|
| 348 |
+ it "nulls out expires_at when keep_events_for is set to 0" do |
|
| 349 |
+ lambda {
|
|
| 350 |
+ @agent.options[:foo] = "bar" |
|
| 351 |
+ @agent.keep_events_for = 0 |
|
| 352 |
+ @agent.save! |
|
| 353 |
+ }.should change { @event.reload.expires_at }.to(nil)
|
|
| 354 |
+ end |
|
| 355 |
+ end |
|
| 279 | 356 |
end |
| 357 |
+ |
|
| 280 | 358 |
end |
| 281 | 359 |
|
| 282 | 360 |
describe "scopes" do |